<?php
// +----------------------------------------------------------------------
// | 杭州十禾信息科技有限公司
// +----------------------------------------------------------------------
// | Copyright (c) 2013-2015 http://www.shelpe.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: LeePuman <ysyzlsm@163.com>
// +----------------------------------------------------------------------

/**
 * 直通快车后台服务。
 */
class ServicesAction extends NewzanAction
{
	
	/** @var ApiModel */
	private $_apiMdl = null;
	/** @var ApiModel2 */
	private $_apiMdl2 = null;
	/** @var CategoryModel */
	private $_cateMdl = null;
	/** @var WordModel */
	private $_wordMdl = null;
	/** @var AutoDriveBaseModel */
	private $_autoDriveMdl = null;
	/** @var KLogger */
	protected $klog = null;
	
	/**
	 * 用来验证请求的合法性。所有service请求需要带一个参数_GET['token']。
	 * token的值为：md5(SERVICE_SECRET . $service_name)。
	 * @var string
	 */
	const SERVICE_SECRET = 'ZTC_SERVICE_FR'; 
	/** 
	 * 关键词的拼接字符：半角逗号。
	 * @var string
	 */
	const WORD_SEP = ',';
	/** @var int 当请求某个词一段时间内的统计属性时,允许的误差天数 */
	const ERR_DAYS_ALLOWED = 2; 
	
	public static $WORD_STAT_PROPS = array('avg_price', 'click', 'competition', 'ctr', 'pv');
	public static $WORD_STAT_ANALYSIS = array('area', 'hprice', 'source');
	public static $WORD_STAT_ALL = array('pv', 'click', 'competition', 'ctr', 'avg_price', 'area', 'source', 'hprice');
	public static $TIMESPAN = array('DAY' => 0, 'WEEK' => 6, 'MONTH' => 29, '3MONTH' => 89);
	
	public function _initialize()
	{
		parent::_initialize();
		
		global $klog;
		if(!$klog) {
			import('@.Library.KLogger');
			$klog = new KLogger(SCRIPT_LOG_DIR . MODULE_NAME . 'Action-' . ACTION_NAME, defined('APP_DEBUG') ? KLogger::DEBUG : KLogger::INFO);
		}
		$this->klog = $klog;
	}
	
	/**
	 * 重写_request方法, 不会处理json.
	 * 
	 * @param string $param
	 * @return null|string
	 */
	protected function _request($param)
	{
		return isset($_REQUEST[$param]) ? $_REQUEST[$param] : null;
	}
	
	/**
	 * 获取某个类目的基础属性的最大值.
	 * TODO 文档
	 * @param int $cate 类目ID.
	 * @return array
	 */
	public function catemaxprops()
	{
		if(empty($_GET['cate_id'])) {
			$this->display_json('必须给定要查询的类目ID。参数为cate_id ', RetStatus::PARAM_ERR);
		}
		$cateId = intval($_GET['cate_id']);
		$res['cate_id'] = $cateId;
		$res['max'] = $this->_getCateMdl()->maxProps($cateId);
		$this->display_json($res);
	}
	
	/**
	 * 获取一系列关键词的统计数据。对于统计属性，返回的是平均值。
	 * 对应的service为keywords.stats。
	 * 
	 * TEST URL: http://s.5dcc.cn/services/keywordsstats?use_api=1&type=avg&fields=pv,click,ctr&duration=WEEK&keywords=%E5%9F%BA%E5%8F%8B
	 * 
	 * @param
	 * 传输参数：	_GET['token']: token = md5(SERVICE_SECRET . $service_name). 
	 * 				_GET['keywords']: 用半角逗号","拼接的字符串。
	 * 				_GET['duration']: 要查询的时间段. DAY|WEEK|MONTH|3MONTH 或者 YYYY-mm-dd:YYYY-mm-dd
	 * 				_GET['fields']: 'pv', 'avg_price', 'competition', 'click', 'ctr', 'area', 'source', 'hprice'
	 * 				_GET['type']: 要查询的类型. avg: 给定时间段内的各属性平均值; trend: 给定时间段内的属性序列.
	 * 
	 * NOTE: 'related_cids' 放到另外的函数当中。
	 * 
	 * @return 如果所有的词都没有结果： 	{"status":200,"response":{"3g":[],"4s":[],"体检卡上海":[]}}
	 * 					如果部分词没有结果：			{"status":200,"response":{"3g":{"pv":262485.83333333,"avg_price":50.333333333333},"体检卡上海":[]}}
	 * 					如果部分属性未找到结果则只显示找到的属性：例如fields=pv,avg_price,area,source，但是area,source没有找到，则只返回上面的结果。
	 * 					如果全部找到，则返回：
	 * 						{ "status":200, 
	 *							"response":{
	 *								"3g":{"pv":279442.90909091,"avg_price":51.545454545455,"area":"{北京,27190,新疆,1585,...}","hprice":"{135,1,210,1,115,1, ... }","source":"{2,152194,1,61224}"},
	 *								"体检卡上海":{"pv":1317.3636363636, "avg_price":729, "area":"{北京,34,重庆,1}", "hprice":"{110,1,800,3,190,1}", "source":"{2,79,1,1076}"}
	 *							}   }
	 */
	public function keywordsstats()
	{
		// 获取参数
		$words = $this->_request('keywords');
		$duration = $this->_request('duration');
		$fields = $this->_request('fields');
		$type = $this->_request('type');
		$useApi = (bool) $this->_request('use_api', null, 0);
		$userId = intval($this->_request('user_id'));
		// 检查参数
		// param 非空
		if($words == '' || empty($duration) || empty($fields)) {
			$this->display_json('关键词、时间段、或属性字段未给定。', RetStatus::PARAM_ERR);	
		}
		// param 'time'
		$dayStart = 0;
		$dayEnd = -1;
		if(strpos($duration, ':') !== false) { // 时间格式为 YYYY-mm-dd:YYYY-mm-dd. NOTE: both dates are inclusive.
			list($dayStart, $dayEnd) = explode(':', $duration);
			$dayStart = strtotime($dayStart);
			$dayEnd = strtotime($dayEnd);
		}
		if(!in_array($duration, self::$TIMESPAN) && $dayStart > $dayEnd) {
			$this->display_json('给定的时间无效。', RetStatus::PARAM_ERR);
		}
		// param $fields
		$fields = explode(',', $fields);
		foreach($fields as $field) {
			if(!in_array($field, self::$WORD_STAT_ALL)) {
				$this->display_json("给定的字段无效。必须为" . join(', ', self::$WORD_STAT_ALL) . "中的一个或者组合。组合由逗号‘,’拼接。", RetStatus::PARAM_ERR);
			}
		}
		$propFields = array_intersect($fields, self::$WORD_STAT_PROPS);
		$analysisFields = array_intersect($fields, self::$WORD_STAT_ANALYSIS);
		// param $type
		if(empty($type))
			$type = 'avg';
		if(!in_array($type, array('avg', 'trend'))) {
			$this->display_json('给定的查询类型无效。只能为avg或者trend。', RetStatus::PARAM_ERR);
		}
		if($type == 'trend' && !empty($analysisFields)) {
			$this->display_json("趋势查询不支持分析属性:  " . join(', ', self::$WORD_STAT_ANALYSIS) . "。", RetStatus::PARAM_ERR);
		}
		if($type == 'trend' && empty($propFields)) {
			$this->display_json("趋势查询必须给定基础属性: " . join(', ', self::$WORD_STAT_PROPS) . "。", RetStatus::PARAM_ERR);
		}
		
		// 处理words参数
		Log::record("keywordsstats() words: $words", Log::DEBUG);
		$words = explode(',', $words);
		
		// 处理时间参数
		if($dayStart == 0) {
			$dayEnd = strtotime(date('Y-m-d')) - 86400; // yesterday (ie. yesterday zero o'clock). NOTE: This day should be inclusive!!!
			$dayStart = $dayEnd - self::$TIMESPAN[$duration] * 86400; // NOTE: should be also inclusive!!!
		}
		if(time() - $dayStart > 91 * 86400) {
			$this->display_json('查询的时间必须在90天以内。', RetStatus::PARAM_ERR);
		}
		// 设置session
		if($userId != 0) {
			$userMdl = new UserModel();
			$userMdl->setAuth($userId);
		}
		
		// 找数据
		if( $type == 'avg' ){
		    $words_avg = array();
		    if( !empty($propFields) ){
		        $words_avg = $this->_getWordMdl()->getAvgProps($words, $propFields, $dayStart, $dayEnd);
		        // dump($words_avg);
		    }
		    $words_analysis = array();
		    if( !empty($analysisFields) ){
		        $words_analysis = $this->_getWordMdl()->getWordAnalysis($words, $analysisFields, $dayStart, $dayEnd);
		    }
		    $res = array_replace_recursive($words_avg , $words_analysis);
		}else{
		    $res = $this->_getWordMdl()->getSerialProps($words, $propFields, $dayStart, $dayEnd);
		}
		// 返回结果
		$this->display_json($res);
	}
	
	/**
	 * 查询相关词服务. 
	 * 查询给定词的相关词, 及给定时间段的基础属性统计信息(平均值). 如果给定了某个基础属性趋势图字段,
	 * 则同时查询给定字段在给定时间段内的时间序列.
	 * 
	 * @param string $_GET['keywords']	要查询的关键词。','拼接的字符串.
	 * @param string $_GET['$order'] 	推荐相关词语的排序方式。必须是'pv', 'click', 'competition', 'ctr', 'avg_price'中的一个。
	 * @param int $_GET['asc'] 				是否升序排列。一般应该是倒序排列。
	 * @param string $_GET['duration']	'DAY', 'WEEK', 'MONTH', '3MONTH'中的一个. 或者 YYYY-mm-dd:YYYY-mm-dd.
	 * @param int $_GET['page']				[可选] 要查询的页面. 从1开始. 默认为1.
	 * @param int $_GET['pagesize']		[可选] 一页内显示的记录条数. 必须>0. 默认为20.
	 * @param string $_GET['trendfield']	[可选] 趋势图的字段. 'pv', 'click', 'competition', 'ctr', 'avg_price'中的一个。
	 * 																如果为空(''), 则不返回趋势图信息. 默认为空('').
	 * @return JSON: 
	 * {
	 * 		"status":200,
	 *		"response":{
	 * 			"order_by":"XX",	// eg:  click
	 *			"asc":boolean,		// eg: false
	 *			"total_page":NN	// eg: 5
	 *			"keywords": [
	 *				{
	 *					"word":"XX",	// eg: "word":"nike",
	 *					"field1":"XX",	// eg: "pv":"4312730.33",
	 *					"field2":"XX",	// eg: "click":"13443.83",
	 *					...
	 *					"trend":[			// 如果未给定trendfield, 则没有该元素. 如果没有结果, 为空数组[].
	 *							{ "date":"YYYY-dd-mm", "field":"NN.NN" } // eg: { "date":"2013-01-19", "pv":"4414634" } 
	 *							{ "date":"YYYY-dd-mm", "field":"NN.NN" } // eg: { "date":"2013-01-20", "pv":"4718925" }
	 *							...
	 *					]
	 *				},
	 *				{
	 *					"word":"XX",	// eg: "word":"nike",
	 *					...
	 *				},
	 *				...
	 *			]
	 *		}
	 * }
	 */
	public function keywordsrelated()
	{
		$words = $this->_request('mykeywords');
		$order = $this->_request('order');
		$asc = $this->_request('asc');
		$duration = $this->_request('duration');
		$page = intval($this->_request('page'));
		$pageSize = intval($this->_request('pagesize'));
		$trendField = $this->_request('trendfield');
		$userId = intval($this->_request('user_id'));
	
		// 检查参数
		if(empty($order)) 
			$order = 'pv';
		if(empty($asc))
			$asc = false;
		if($words == ''  ) { // || !in_array($order, self::$WORD_STAT_PROPS)
			$this->display_json('要查询的关键词不能为空。参数为mykeywords', RetStatus::PARAM_ERR);
		}
		// 参数:时间
		$timeSpanAllowed = array_keys(self::$TIMESPAN);
		if(empty($duration)) {
			$duration = 'WEEK';
		}
		if(!in_array($duration, $timeSpanAllowed)) {
			$this->display_json("给定的时间无效. 必须为" . join(', ', $timeSpanAllowed) . "中的一个. ", RetStatus::PARAM_ERR);
		}
		if(!empty($trendField) && !in_array($trendField, self::$WORD_STAT_PROPS)) {
			$this->display_json("趋势图字段无效. 必须为" . join(', ', self::$WORD_STAT_PROPS . '中的一个.'), RetStatus::PARAM_ERR);
		}
		// 参数:分页
		if(!$page || $page <= 0) 
			$page = 1;
		if(!$pageSize || $pageSize <= 0) 
			$pageSize = 20;
		
		// 设置session
		if($userId != 0) {
			$userMdl = new UserModel();
			$userMdl->setAuth($userId);
		}
		
		// dump($words);
		// 缓存, 分页, 获取属性值. 有两种缓存方法: 
		// 法一: 保存未经排序的相关词. 每次查询的时候再进行排序. 该法的优点是可能会节约缓存空间, 但是查询要排序,会比较耗时.
		// $relatedWords = S("s_related_$words");
		// if(false === $relatedWords)
		// {
		// 	$relatedWords = $this->_getWordMdl()->getSortedRelatedWords2(
		// 	$this->_getApiMdl(), $words, '', false, true, $duration);
		// 	S("s_related_$words", $relatedWords, 600); // expires after 10min
		// }
		// usort($relatedWords, "uc_cmp_$order" . '_' . ($asc ? 'asc' : 'dec'));

		// 法二: 保存排序过的相关词. 该法的优点是查询省时, 但是可能会比较费缓存空间.
		$allRelatedWordProps = S("s_related_$words_$order_$asc");
		if(false === $allRelatedWordProps) {
			$allRelatedWordProps = $this->_getWordMdl()->getSortedRelatedWords2($this->_getApiMdl(), $words, $order, $asc, true, $duration);
			S("s_related_$words_$order_$asc", $allRelatedWordProps, 300); // expires after 5min
		}
		// 分页
		$pageStart = ($page - 1) * $pageSize;
		// $pageRelatedWordProps = array_slice($allRelatedWordProps, $pageStart, $pageSize);
		$pageRelatedWordProps = $allRelatedWordProps;
		// 查询趋势
		if($trendField && !empty($pageRelatedWordProps)) {
			$dayEnd = strtotime(date('Y-m-d')) - 86400; // yesterday (ie. yesterday zero o'clock). NOTE: This day should be inclusive!!!
			$dayStart = $dayEnd - self::$TIMESPAN[$duration] * 86400; // NOTE: should be also inclusive!!!
			// echo '--- trend ---' . PHP_EOL;
			$pageRelatedWords = array_column($pageRelatedWordProps, 'word');
			$wordsTrend = $this->_getWordMdl()->getSerialProps($pageRelatedWords, array($trendField), $dayStart, $dayEnd);
			// dump($wordsTrend);
			foreach ($pageRelatedWordProps as &$wordProps) {
				$word = $wordProps['word'];
				$wordProps['trend'] = $wordsTrend[$word];
			}
		}
		
		// 返回结果
		$res = array(
			'keywords' => $pageRelatedWordProps, //$relatedWordsProps, // $relatedWords中还需要加入 pv、click等信息。
			'order_by' => $order,
			'asc' => $asc,
			// 'total_page' => ceil(count($allRelatedWordProps) / $pageSize),
			'total_page' => 1,
		);
		
		$this->display_json($res);
	}
	
	/**
	 * 智能推荐词
	 * 
	 * TODO 将自定义关键词mykeywords加入数据库.
	 * 
	 */
	public function keywordsrcmm()
	{
		// 获取参数
		$userId = intval($this->_request('user_id'));
		$adgroupId = intval($this->_request('adgroup_id'));
		$page = intval($this->_request('page'));
		$pageSize = intval($this->_request('pagesize'));
		$order = $this->_request('order') ? $this->_request('order') : 'smart'; // 默认为智能排序
		$asc = $this->_request('asc');
		$asc = ($asc && strtolower($asc) !== 'false') ? true : false;
		$priceTypeId = $this->_request('price_type') ? intval($this->_request('price_type')) 
				: AutoDriveBaseModel::DEFAULT_PRICE_TYPE; // 传过来的为数字。
		$priceType = isset(AutoDriveBaseModel::$PRICE_TYPES[$priceTypeId]) 
				? AutoDriveBaseModel::$PRICE_TYPES[$priceTypeId]
				: AutoDriveBaseModel::$PRICE_TYPES[AutoDriveBaseModel::DEFAULT_PRICE_TYPE];
		
		// 检查参数
		if(empty($userId))
			$this->display_json('用户ID(user_id)必须提供。', RetStatus::PARAM_ERR);
		if(empty($adgroupId))
			$this->display_json('推广组ID(adgroup_id)必须提供。', RetStatus::PARAM_ERR);
		$adgroupMdl = new AdgroupModel();
		$adgroup = $adgroupMdl->getAdgroupByAdgroupId($userId, $adgroupId);
		if(!$adgroup)
			$this->display_json('给定的Adgroup不存在。', RetStatus::PARAM_ERR);
		$keywordMdl = new KeywordModel();
		// $existsKeywords = $keywordMdl->getAdgroupKeywords($userId, $adgroupId, false);
		// $this->klog->logDebug('$existsKeywords: \n' . json_encode($existsKeywords));
		// $existsKeywords = array_column( $exists_keywords, 'word');
		$existsKeywords = $keywordMdl->getAdgroupWordsOnly($userId, $adgroupId);
        $this->klog->logDebug('$existsKeywords: \n' . json_encode($existsKeywords));
       
        // set session. 这个可能调用carine::getTbStats()。而carine需要session。
        $userMdl = new UserModel();
        $userMdl->setAuth($userId);
        
		// 参数:分页
		if(!$page || $page <= 0)
			$page = 1;
		if(!$pageSize || $pageSize <= 0)
			$pageSize = 20;

		$duration = 'WEEK';
		$cacheTime = 600;
		
		$rawCacheKey = md5("rcmm-$adgroupId-$priceType");
		$sortedCacheKey = md5("$rawCacheKey-$order-$asc");
		$sortedAllWordProps = S($sortedCacheKey);
		
		if(!$sortedAllWordProps) { // false === $sortedAllWordProps
			$allRelatedWordProps = S($rawCacheKey);
			if(!$allRelatedWordProps) { // false === 
				
				// $autoDriveMdlClass = 'AutoDrive' . DEFAULT_AUTO_ENGINE . 'Model';
				$autoDriveMdl = $this->_getAutoDriveMdl();
				// $autoDriveMdl= new AutoDriveV2Model();
				$wordWithPrice = $autoDriveMdl->init($adgroup, $userId, null, $priceType)
						->prepareWords($existsKeywords)
						->filterWords()
						->sortWords()
						->selectWords(null, -1);
				$this->klog->logDebug('$wordWithPrice: \n' . json_encode($wordWithPrice));
				
				// $tbStats = $keywordMdl->getTbStats(array_keys($wordWithPrice),
				// 		'pv,avg_price,competition,click,ctr',
				// 		date('Y-m-d', 7 - 86400), date('Y-m-d', time() - 86400), true);
				$tbStats = $autoDriveMdl->getDriveData('WordTbStats3Days');
				
				// 将统计属性和价格整合并缓存
				$zeroStat = array('pv'=>0, 'avg_price'=>0, 'competition'=>0, 'click'=>0, 'ctr'=>0);
				$allRelatedWordProps = array();
				foreach($wordWithPrice as $word => &$wordPrice) {
					$wordStat = $tbStats[$word] ? $tbStats[$word] : $zeroStat;
					unset($wordPrice['matchscope']);
					$allRelatedWordProps[] = array_merge($wordStat, $wordPrice);
				}
				
				S($rawCacheKey, $allRelatedWordProps, $cacheTime); // expires in 10min
			} 
			
			$this->klog->logDebug('$allRelatedWordProps: \n' . json_encode($allRelatedWordProps));
				
			// 按指定的字段和方式排序
			if('smart' != $order) {
				$orderMethod = "uc_cmp_$order" . '_' . ($asc ? 'asc' : 'dec');
				usort($allRelatedWordProps, $orderMethod); // 自定义排序的方法定义在 Application/Common/common.php中.
			}
			
			$sortedAllWordProps = $allRelatedWordProps;
			
			S($sortedCacheKey, $sortedAllWordProps, $cacheTime / 2); // expires after 5min
			
		}
		
		// 分页
		$pageStart = ($page - 1) * $pageSize;
		// $pageRelatedWordProps = array_slice($sortedAllWordProps, $pageStart, $pageSize);
		$pageRelatedWordProps = $sortedAllWordProps;
		
		// 返回结果
		$res = array(
				// 'type' => $rcmmType,
				// 'category_id' => $cateId,
				// 'max_price' => $maxPrice,
				// 'price_ratio' => $priceRatio,
				// 'mykeywords' => $this->_request('mykeywords'),
				// 'content' => $this->_request('content'),
				'price_type' => $this->_request('price_type'),
				'keywords' => $pageRelatedWordProps,
				// 'total_page' => ceil(count($sortedAllWordProps) / $pageSize),
				'total_page' => 1,
		);
		$this->klog->logDebug('$res: \n' . json_encode($res));
		$this->display_json($res); 
	}

	/**
	 * 同步用户报表服务. 主要目的是为了可以使用curl请求来使初始化并行化.
	 * 
	 * 需要给定5个参数(通过GET或者POST均可):
	 * @param string $model 模型类型. 区分大小写. 例如: RptAdgroupModel
	 * @param string $method 调用的方法. 区分大小写. 例如: syncAdgroupReport. 目前方法必须以sync开头, 以保证安全.
	 * @param string $access_token 当前用户或者授权给的用户的access_token, 必须在有效时间以内. 跟user_id一起用来验证操作合法性.
	 * @param int $user_id 当前操作的用户. 跟access_token一起用来验证操作合法性.
	 * @param array_in_json_string $params [可选] $model::$method()所需要的参数. 必须按照顺序给. 数组的键值必须为0开始的自然数.
	 * 			如果所调用方法存在参数, 则必须给定, 否则可以不给. 
	 */
	public function syncrpt()
	{
		//echo $this->_request('id') . ' ' . PHP_EOL;
		$mdl = $this->_request('model');
		$method = $this->_request('method');
		$accessToken = $this->_request('access_token');
		$userId = $this->_request('user_id');
		$params = $_REQUEST['params'] ? json_decode( $_REQUEST['params']) : array();
		// 检查参数
		if(!$accessToken) {
			$this->display_json("必须给定access_token.", RetStatus::PARAM_ERR);
		}
		if(empty($userId)) {
			$this->display_json("必须给定user_id.", RetStatus::PARAM_ERR);
		}
		if(strpos($method, 'sync') !== 0) {
			$this->display_json("只支持sync方法.", RetStatus::PARAM_ERR);
		}
		$methodExists = false;
		$mustHaveArgNum = 0;
		// require_once LIB_PATH . "Model/$mdl.class.php";
		if(method_exists($mdl, $method)) {
			$reflection = new ReflectionMethod($mdl, $method);
			if ($reflection->isPublic()) {
				$methodExists = true;
			}
			$mthdArgs = $reflection->getParameters();
			foreach ($mthdArgs as $thdArg) {
				if(!$thdArg->isOptional())
					++$mustHaveArgNum;
				else
					break;
			}
		}
		if(!$methodExists) {
			$this->display_json("$mdl::$method()不存在或者不为public.", RetStatus::PARAM_ERR);
		}

		if(count($params) < $mustHaveArgNum) {
			$this->display_json("给定的参数个数不够$mdl::$method()的必需参数.", RetStatus::PARAM_ERR);
		}
		if(!$params) // $params必须给定为数组, 不能不给, 也不能为null
			$params = array();
		
		// 检查权限并且创建临时session
		$userMdl = D('User');
		$me = $userMdl->getUserByTaobaoId($userId);
		if(!$me)
			$this->display_json("给定的用户不存在.", RetStatus::PARAM_ERR);
		$managerId = $me['authorized_to'];
		$oauth = M('oauth')->where(array('taobao_user_id' => ($managerId ? $managerId : $userId)))->find();
		if(!$oauth || $oauth['access_token'] != $accessToken || ($oauth['oauth_time'] + $oauth['expires_in'] < time())) {
			$this->display_json("access_token无效或者已经过期. 给定的access_token为: $accessToken", RetStatus::PARAM_ERR);
		} else {
			$me['oauth'] = $oauth;
			session('me' , $me);
			if($managerId) {
				$manager = $userMdl->getUserByTaobaoId($managerId);
				if(!$manager){
					$this->display_json("授权账户不存在.", RetStatus::PARAM_ERR);
				}
				$manager['oauth'] = $oauth;
				session('manager', $manager);
			}
		}
		
		// 返回结果然后调用函数
		$this->display_json_fastcgi('OK');
		my_fastcgi_finish_request();
		
		$this->klog->logInfo("service syncrpt() on $mdl::$method() with params: " . join(', ', $params));
		try {
			$mdl = new $mdl(); // 此时创建的model在_intialize()中才会有session('me')
			call_user_func_array(array($mdl, $method), $params);
		} catch (Exception $e) {
			$this->klog->logInfo($e->getMessage());
		}
		
		// 删除临时session
		session('me' , null);
		session('manager', null);
	}
	
	
	/**
	 * 相关词查询  给定词列表，查询对应相关词。
	 * @param：String $words 词列表。（用逗号拼接）。示例：”篮球鞋,休闲皮鞋,黑皮鞋”。
	 * @return
	 * 正常返回结果是：
	 * Array(string=>array(string))编码成Json格式。示例：
	 * {“篮球鞋”:[“球鞋”,”nike篮球鞋”],
	 * ”休闲皮鞋”: [“皮鞋”,”七匹狼皮鞋”,”黑色皮鞋”],
	 * ”黑皮鞋”: [“皮鞋”,”七匹狼皮鞋”,,” 休闲皮鞋”]
	 * }
	 * 当参数为空时：返回为空数组,并给出参数错误值
	 * 当获取api后为空值时，返回空数据，并提示未找到错误值
	 */
	public function apirelatedwords()
	{
		$words = I('words'); // http://ztc.newzuan.cn/Services/apibasic?words=男鞋,女鞋,nike鞋
		if (!$words) {
			$this->display_json(array(),RetStatus::PARAM_ERR);
		}
		$words = explode(',', $words);
		if (!$words) {
			$this->display_json(array(),RetStatus::PARAM_ERR);
		}
		$res = $this->_getApiMdl2()->getRelatedWords($words);
		if ( $res != null ) {
			$this->display_json($res , RetStatus::OK);
		} else {
			$this->display_json(array(), RetStatus::NOT_FOUND);
		}
	}
	
	
	/**
	 * 基础属性查询. 给定词列表，查询其基础属性。
	 * @param string $words 词列表。（用逗号拼接）。示例：”篮球鞋,休闲皮鞋,黑皮鞋”。
	 * @return
	 * 正常返回结果是：
	 * Array(string=>array(k=>v)) 编码成Json格式。示例：
	 * {
	 * “篮球鞋”:[“pv”:8888,”click”:88,”avg_price”:322, … ],
	 * ”休闲皮鞋”: [“pv”:8888,”click”:88,”avg_price”:322, … ],
	 * ”黑皮鞋”: [“pv”:8888,”click”:88,”avg_price”:322, … ]
	 *　}
	 * 当参数为null时。返回为null数组，并提示参数错误返回值
	 * 当调用api后未获得返回的结果时 返回字符串 “未获取该词的基础属性词”,并给出api未给出该结果
	 */
	public function apibasicprops()
	{
		$words = I('words'); // http://ztc.newzuan.cn/ServicesS/apibasicattributes?words=男鞋,女鞋,nike鞋
		if (!$words) {
			$this->display_json(array(),RetStatus::PARAM_ERR);
		}
		$words = explode(',', $words);
		if (!$words) {
			$this->display_json(array(),RetStatus::PARAM_ERR);
		}
		$res = $this->_getApiMdl2()->getBigWordProps($words);
		if ($res != null) {
			$this->display_json($res , RetStatus::OK);
		} else {
			$this->display_json('未获取该词的基础属性词' , RetStatus::NOT_FOUND);
		}
	}
	
	
	/**
	 * 获取ApiModel。防止创建多个实例。
	 * @return ApiModel 实例化过的ApiModel。
	 */
	private function _getApiMdl()
	{
		if($this->_apiMdl === null) {
			$this->_apiMdl = new ApiModel();
		}
		return $this->_apiMdl;
	}
	
	/**
	 * 获取ApiModel2。防止创建多个实例。
	 * @return ApiModel 实例化过的ApiModel。
	 */
	private function _getApiMdl2()
	{
		if($this->_apiMdl2 === null) {
			$this->_apiMdl2 = new Api2Model();
		}
		return $this->_apiMdl2;
	}
	
	/**
	 * 获取CategoryModel。防止创建多个实例。
	 * @return CategoryModel 实例化过的CategoryModel。
	 */
	private function _getCateMdl()
	{
		if($this->_cateMdl === null) {
			$this->_cateMdl = new CategoryModel();
		}
		return $this->_cateMdl;
	}
	
	/**
	 * 获取WordModel。防止创建多个实例。
	 * @return WordModel 实例化过的WordModel。
	 */
	private function _getWordMdl()
	{
		if($this->_wordMdl === null) {
			$this->_wordMdl = new WordModel();
		}
		return $this->_wordMdl;
	}
	
	/**
	 * @return AutoDriveBaseModel
	 */
	private function _getAutoDriveMdl()
	{
		if($this->_autoDriveMdl === null) {
			$autoDriveMdlClass = 'AutoDrive' . DEFAULT_AUTO_ENGINE . 'Model';
			$this->_autoDriveMdl = new $autoDriveMdlClass();
		}
		return $this->_autoDriveMdl;
	}
	
	public function display_json($r , $code = RetStatus::OK)
	{
		$dom = $this->_get('dom');
		echo json_encode(array(
				'status' => $code,
				'response' => $r,
		));
		exit;
	}
}

/**
 * 智能淘词方式
 * @author weiping
 */
class KeywordRcmmType {
	const BALANCED = 1;
	const RELATED = 2;
	const COMBINED = 3;
}
